import * as THREE from "three"
import CameraModule from "../module/CameraModule"
import Vectors from "../three/Vectors"
import funcUtil from "../utils/func.util"
import OpenDeferred from "../utils/OpenDeferred"
import CommonControls from "./CommonControls"
import o from "../math/45870"
export default class PanoramaCameraControls extends CommonControls {
  lookVelocity: THREE.Vector2
  lookAccel: THREE.Vector2
  tempAxis: THREE.Vector3
  tempOrientation: THREE.Quaternion
  currentOrientation: THREE.Quaternion
  tempEuler: THREE.Euler
  cameraModule: CameraModule
  transition: { active: boolean; startTime: number; elapsed: number; duration: number; velocity: THREE.Vector2; easeOut: boolean; deferred?: OpenDeferred }
  constructor(e) {
    super()
    this.lookVelocity = new THREE.Vector2()
    this.lookAccel = new THREE.Vector2()
    this.tempAxis = new THREE.Vector3()
    this.tempOrientation = new THREE.Quaternion()
    this.currentOrientation = new THREE.Quaternion()
    this.tempEuler = new THREE.Euler()
    this.cameraModule = e
    this.transition = {
      active: !1,
      startTime: 0,
      elapsed: 0,
      duration: 0,
      velocity: new THREE.Vector2(),
      easeOut: !1
    }
  }
  setLookAcceleration(e, t = !1) {
    this.transition.active ||
      (t &&
        (e.x && this.lookVelocity.x && Math.sign(e.x) !== Math.sign(this.lookVelocity.x) && (this.lookVelocity.x = 0),
        e.y && this.lookVelocity.y && Math.sign(e.y) !== Math.sign(this.lookVelocity.y) && (this.lookVelocity.y = 0)),
      (this.lookAccel.x = void 0 !== e.x ? e.x : this.lookAccel.x),
      (this.lookAccel.y = void 0 !== e.y ? e.y : this.lookAccel.y))
  }
  startTransition(e, t, i) {
    const n = new OpenDeferred()
    this.transition.active = !0
    this.transition.duration = e
    this.transition.elapsed = 0
    this.transition.startTime = Date.now()
    this.transition.deferred = n
    this.transition.velocity.copy(t)
    this.transition.easeOut = i
    this.lookAccel.set(0, 0)
    this.lookVelocity.copy(t)
    this.cameraModule.beginExternalTransition()
    return n.promise()
  }
  stopTransition() {
    this.transition.active && (this.cameraModule.endExternalTransition(), (this.transition.active = !1)),
      this.transition.deferred && (this.transition.deferred.resolve(), (this.transition.deferred = void 0))
  }
  updateTransition(e) {
    const t = e / o.SI
    if ((this.lookVelocity.copy(this.transition.velocity), (this.transition.elapsed += e), this.transition.elapsed >= this.transition.duration)) {
      const t = this.transition.duration - (this.transition.elapsed - e)
      this.lookVelocity.multiplyScalar(t / e)
    } else this.lookVelocity.multiplyScalar(t)
  }
  updateCameraParameters() {
    const e = this.cameraModule.getData(!1)
    this.tempEuler.setFromQuaternion(e.pose.rotation, "YXZ")
    const t = this.tempEuler.x,
      i = funcUtil.getMax(this.lookVelocity.y, o.zf - t, o.uQ - t)
    this.tempAxis.copy(Vectors.RIGHT),
      this.tempOrientation.setFromAxisAngle(this.tempAxis.applyQuaternion(e.pose.rotation), i),
      this.currentOrientation.copy(e.pose.rotation).premultiply(this.tempOrientation),
      this.tempOrientation.setFromAxisAngle(Vectors.UP, this.lookVelocity.x),
      this.currentOrientation.premultiply(this.tempOrientation),
      e.pose.rotation.equals(this.currentOrientation) ||
        (this.tempOrientation.copy(this.currentOrientation).normalize(), this.cameraModule.updateCameraRotation(this.tempOrientation))
  }
  update(e) {
    const t = this.cameraModule.getData(!1),
      i = e / o.SI
    t.pose.rotation.equals(this.currentOrientation) || this.currentOrientation.copy(t.pose.rotation),
      this.transition.active
        ? (this.updateTransition(e),
          this.updateCameraParameters(),
          this.transition.elapsed >= this.transition.duration && (this.stop(this.transition.easeOut), (this.transition.active = !1)))
        : (this.lookAccel.length() > 0.00001 || this.lookVelocity.length() > 0.00001) &&
          (this.lookVelocity.addScaledVector(this.lookAccel, i), this.updateCameraParameters(), this.lookVelocity.multiplyScalar(Math.pow(1 - o.O8, i)))
  }
  stop(e = !1) {
    this.stopTransition(), this.lookAccel.set(0, 0), e || this.lookVelocity.set(0, 0)
  }
  startRotateTransition(e, t, i) {
    const n = this.cameraModule.getData(!1)
    return (
      n.transition && n.transition.activeInternal && n.transition.to.rotation && (n.transition.to.rotation = void 0),
      this.startTransition(e, t.clone().multiplyScalar(o.SI), i).nativePromise()
    )
  }
  startTranslateTransition(e, t, i = !0) {
    throw new Error("Panning isn't supported in Panorama Controls")
  }
  startZoomTransition(e, t, i) {
    throw new Error("Zooming isn't supported in Panorama Controls")
  }
}
